﻿using IronPython.Runtime.Operations;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils.NPOIHelp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using static IronPython.Modules._ast;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// 由BaseQuestion拆分而成，如果一个Base中只有一个填充项，则会被拆分成一个Question。
    /// BaseQuestion的Body（题干）中有几个填空项，就应该有几个答案列表。
    public class Question
    {
        private List<ChoiceItem> choiceItems = new List<ChoiceItem>();
        /// <summary>
        /// 选择支列表。
        /// </summary>
        public List<ChoiceItem> ChoiceItems
        {
            get { return this.choiceItems; }
        }

        /// <summary>
        /// 为便于脚本调用，提供一个别名（亦名题支列表）。
        /// </summary>
        public List<ChoiceItem> 选择项列表
        {
            get { return this.choiceItems; }
        }

        /// <summary>
        /// 为便于脚本调用，提供一个别名（亦名选择项列表）。
        /// </summary>
        public List<ChoiceItem> 题支列表
        {
            get { return this.choiceItems; }
        }

        /// <summary>
        /// 防止演示时总是混淆顺序。
        /// </summary>
        public bool ChoiceItemsConfounded { get; set; } = false;

        public bool 是否混淆选项顺序
        {
            get { return ChoiceItemsConfounded; }
            set { ChoiceItemsConfounded = value; }
        }

        private string answer = string.Empty;
        /// <summary>
        /// 仅适用于选择题（包括判断题）、填空题。不适用于主观题。
        /// </summary>
        public string Answer
        {
            get
            {
                if (choiceItems.Count <= 0)
                {
                    if (string.IsNullOrEmpty(this.titleString)) return string.Empty;

                    //填空题                    
                    var reg = new Regex(@"(?<=【).{1,}?(?=】)");
                    var matches = reg.Matches(this.titleString);

                    if (matches.Count > 0)
                    {
                        var trimchars = new char[] { ' ', '　', '\t', };
                        var sb = new StringBuilder();
                        sb.Append("【");
                        for (int i = 0; i < matches.Count; i++)
                        {
                            var m = matches[i];
                            if (m.Length > 0)
                            {
                                sb.Append($"({(i + 1).ToString()})");
                                sb.Append(m.Value.Trim(trimchars));
                                sb.Append("】【");
                            }
                        }
                        sb.Append("】");
                        answer = $"【{sb.ToString().Trim(new char[] { '【', '】', })}】";
                    }
                    else answer = "";
                }

                return this.answer;
            }
            set { answer = value; }
        }

        public string 答案文本
        {
            get { return Answer; }
            set { answer = value; }
        }

        public void 混淆题支顺序()
        {
            //先需要混淆下顺序，否则会总选A了。
            int[] randomArray = L.GetRandomIntArray(this.ChoiceItems.Count);
            for (int i = 0; i < randomArray.Length; i++)
            {
                var qi = this.ChoiceItems[i];
                qi.OrderNumber = randomArray[i];
            }

            this.choiceItems.Sort(new ComparsionQuestionItem());

            this.ChoiceItemsConfounded = true;

            var answerOrderNum = -1;
            var trimChars = new char[] { ' ', '　', '\t', '\r', '\n' };

            for (int i = 0; i < this.choiceItems.Count; i++)
            {
                var ci = this.choiceItems[i];
                ci.OrderNumber = i + 1;
                if (ci.IsAnswer)
                {
                    this.Answer = ci.选择项文本.Trim(trimChars);
                    answerOrderNum = i;
                    break;
                }
            }

            this.AnswerNumMark = L.ConvertToAlpha(answerOrderNum + 1);
        }

        /// <summary>
        /// 选择题答案（A、B、C等）。
        /// </summary>
        public string AnswerNumMark { get; set; }

        public string 答案标记
        {
            get
            {
                return AnswerNumMark;
            }
            set
            {
                AnswerNumMark = value;
                if (AnswerNumMark.EndsWith("．") == false)
                {
                    AnswerNumMark += "．";
                }
            }
        }
        public string 答案字母
        {
            get
            {
                return AnswerNumMark.Trim(new char[] { ' ', '　', '\t', '\r', '\n', '．', });
            }
        }

        public string 编号答案文本
        {
            get
            {
                if (string.IsNullOrWhiteSpace(AnswerNumMark))
                {
                    return 答案文本;
                }

                return 答案标记 + 答案文本;
            }
        }

        /// <summary>
        /// [构造方法]用于生成一道只有题干的试题。
        /// </summary>
        /// <param name="titleString">试题的题干文本。</param>
        public Question(string titleString)
        {
            this.TitleString = titleString;
        }

        /// <summary>
        /// [构造方法]生成一道试题。适用于填空题（无答案）、选择题（有答案项、错项，）、判断题（特殊的选择题）。
        /// </summary>
        /// <param name="titleString">应传入由BaseQuestion.BuildQuestionTitles()方法生成的String[]的某个成员。
        /// 它的形式是一行带一个填空的文本。</param>
        /// <param name="bodyString">应传入每个试题的原文本的“答案”部分，
        /// 每个答案文本片段中应包含一个“答案”及一个或多个“错项”。</param>
        public Question(string titleString, string bodyString, bool shouldRandom = true)
        {
            this.TitleString = titleString;

            if (string.IsNullOrEmpty(bodyString)) return;//填空题，有填充项而无答案项。

            var indexOfQuestionEnd = bodyString.IndexOf("〓〓〓〓〓〓");
            if (indexOfQuestionEnd >= 0)
            {
                bodyString = bodyString.Substring(0, indexOfQuestionEnd);
            }

            // 根据bodyStrings生成BaseQuestionItems
            String[] itemStrings = bodyString.Split(new string[1] { "错项＞＞" }, StringSplitOptions.None);
            // 第一个片段是答案，其余每个片段都是“错项”。

            if (itemStrings.Length > 1)
            {
                ChoiceItem answerChoiceItem = new ChoiceItem();
                answerChoiceItem.IsAnswer = true;

                int indexOfAnalysis = itemStrings[0].IndexOf("解析＞＞");
                if (indexOfAnalysis > -1)
                {
                    var tmp = itemStrings[0].Substring(0, indexOfAnalysis).TrimStart(new char[] { '\r', '\n', });
                    if (tmp.StartsWith("　　答案＞＞"))
                    {
                        answerChoiceItem.Text = tmp.Substring(6);
                    }
                    else
                    {
                        answerChoiceItem.Text = tmp;
                    }
                    answerChoiceItem.AnalysisText = L.FormatChoiceQuestionTextString(itemStrings[0].Substring(indexOfAnalysis + 4));
                }
                else
                {
                    var tmp = itemStrings[0].Trim(new char[] { '\r', '\n', '\t', ' ', '　', });
                    if (tmp.StartsWith("答案＞＞"))
                    {
                        answerChoiceItem.Text = tmp.Substring(4);
                    }
                    else
                    {
                        answerChoiceItem.Text = tmp;
                    }
                }

                // CheckPoints 应该是解析的一部分！只是单独占一行书写而已！！
                int indexOfCheckPoints = answerChoiceItem.AnalysisText.IndexOf("考查点＞＞");
                if (indexOfCheckPoints > -1)
                {
                    answerChoiceItem.CheckPoints = answerChoiceItem.AnalysisText.Substring(indexOfCheckPoints + 5);  // 注意顺序
                    answerChoiceItem.AnalysisText = answerChoiceItem.AnalysisText.Substring(0, indexOfCheckPoints);
                }

                this.choiceItems.Add(answerChoiceItem);
                this.answer = answerChoiceItem.Text;

                for (int ii = 1; ii < itemStrings.Length; ii++)
                {
                    ChoiceItem otherChoiceItem = new ChoiceItem();

                    indexOfAnalysis = -1;
                    // 索引从1开始。
                    indexOfAnalysis = itemStrings[ii].IndexOf("解析＞＞");
                    if (indexOfAnalysis > -1)
                    {
                        otherChoiceItem.Text = L.FormatChoiceQuestionTextString(itemStrings[ii].Substring(0, indexOfAnalysis));

                        otherChoiceItem.AnalysisText = L.FormatChoiceQuestionTextString(itemStrings[ii].Substring(indexOfAnalysis + 4));

                        var indexOfOtherCheckPoints = otherChoiceItem.AnalysisText.IndexOf("考查点＞＞");
                        if (indexOfOtherCheckPoints > -1)
                        {
                            otherChoiceItem.CheckPoints = otherChoiceItem.AnalysisText.Substring(indexOfOtherCheckPoints + 5);
                            otherChoiceItem.AnalysisText = otherChoiceItem.AnalysisText.Substring(0, indexOfOtherCheckPoints);     // 注意顺序
                        }
                    }
                    else
                    {
                        otherChoiceItem.Text = L.FormatChoiceQuestionTextString(itemStrings[ii]);
                    }

                    this.choiceItems.Add(otherChoiceItem);
                }
            }

            if (shouldRandom)
            {
                // 使用随机数混淆顺序。
                int[] random = L.GetRandomIntArray(choiceItems.Count);
                for (int i = 0; i < choiceItems.Count; i++)
                {
                    choiceItems[i].OrderNumber = random[i];
                }
                choiceItems.Sort(new ComparsionQuestionItem());
            }
        }

        /// <summary>
        /// [构造方法]适用于
        /// </summary>
        /// <param name="titleString"></param>
        /// <param name="titleAndAnswers"></param>
        public Question(string titleString, string[] titleAndAnswers)
        {
            this.TitleString = titleString;   //有些初始化必须用属性，否则有图像也显示不出来

            this.answers.Clear();
            for (int i = 1; i < titleAndAnswers.Length; i++)
            {
                this.answers.Add(titleAndAnswers[i]);
            }
        }

        /// <summary>
        /// 试题类型：判断、选择、主观。
        /// </summary>
        public QuestionType Type
        {
            get
            {
                if (this.choiceItems.Count <= 0)
                {
                    if (this.answers.Count > 0) return QuestionType.Subjective;

                    if (this.titleString.Contains("【") == false || this.titleString.Contains("】") == false) return QuestionType.Text;//没有任何填空项，只呈现纯文本。

                    return QuestionType.FillBlank;
                }
                else
                {
                    //判断题是只有两个选项的选择题，且两个选项必须是：一个“正确”、一个“错误”。
                    if (this.choiceItems.Count == 2)
                    {
                        if ((this.choiceItems[0].Text == "正确" && this.choiceItems[1].Text == "错误") ||
                            (this.choiceItems[0].Text == "错误" && this.choiceItems[1].Text == "正确"))
                            return QuestionType.Judge;
                    }
                    return QuestionType.Choice;
                }
            }
        }

        /// <summary>
        /// 判断文本行是否属于与试题相关的行。
        /// </summary>
        /// <param name="lineText">源文本。</param>
        /// <returns></returns>
        public static bool IsExamTextLine(string lineText)
        {
            return (lineText.StartsWith("　　试题＞＞") || lineText.StartsWith("＜＜材料＞＞") || lineText.StartsWith("＜＜出处＞＞") ||
                    lineText.StartsWith("＜＜问题＞＞") || lineText.StartsWith("　　答案＞＞") || lineText.StartsWith("　　解析＞＞") ||
                    lineText.StartsWith("　　错项＞＞"));
        }

        /// <summary>
        /// 将试题文本编译为Html标签文本。（编译试题）
        /// </summary>
        /// <param name="questionText">试题内容文本。</param>
        /// <param name="withoutScripts">不带Js脚本、“复制”链接、解析面板等。（用于调用 Reveal.js 演示时）</param>
        public static string ConvertQuestionsToHtml(string questionText, ref int choiceQuestionsCount, bool withoutScripts = false,
            bool forEpub = false, string test_paper_id = "", string markdownFilePath = "")
        {
            choiceQuestionsCount = 0;
            if (string.IsNullOrEmpty(questionText)) return "";

            // 去除辅助序号、将简化填空改写为复合填空。
            var lines = questionText.Replace("\r\n", "\n").Split(new char[] { '\n', }, StringSplitOptions.None);
            var sbuild = new StringBuilder();
            var regNumber = new Regex(@"^[\(（]\.\d{0,}\.[\)）]");
            var regDoubleQuestion = new Regex(@"^[\?？]{2,}[ 　\t]{0,}");
            for (int i = 0; i < lines.Length; i++)
            {
                var line = lines[i];
                if (new Regex(@"^[ 　]{4,}|\t").Match(line).Success)
                {
                    sbuild.Append(lines[i] + "\r\n");  // 保持原样，防止与代码块冲突
                    continue;
                }

                line = line.Trim(new char[] { ' ', '　', '\t', });
                if (line.StartsWith("试题＞＞"))
                {
                    line = line.Substring(4);
                    var matchNumber = regNumber.Match(line);
                    if (matchNumber.Success)
                    {
                        sbuild.Append("　　试题＞＞" + line.Substring(matchNumber.Length) + "\r\n");
                    }
                    else
                    {
                        sbuild.Append("　　试题＞＞" + line + "\r\n");
                    }
                    continue;
                }
                // 双冒号会冲突，改成双问号。
                else if (regDoubleQuestion.Match(line).Success)
                {
                    if (Globals.MainWindow.CompileSimpleFillBlank == false || line.Contains('`') == false)
                    {
                        sbuild.Append(line.TrimStart(new char[] { '?', '？' }) + "\r\n");
                        continue;
                    }

                    var pieces = Utils.Text.SplitToPiecesByRegex(@"`.{1,}?`", line.TrimStart(new char[] { ' ', '?', '？' }));
                    sbuild.Append("　　试题＞＞");
                    foreach (var piece in pieces)
                    {
                        if (piece.IsMartchText)
                        {
                            sbuild.Append("【" + piece.SourceText.Trim(new char[] { ' ', '`' }) + "】");
                        }
                        else
                        {
                            sbuild.Append(piece.SourceText.Trim());
                        }
                    }
                    sbuild.Append("\r\n〓〓〓〓〓〓\r\n\r\n");
                    continue;
                }
                else
                {
                    sbuild.Append(lines[i] + "\r\n");  // 保持原样
                    continue;
                }
            }

            questionText = sbuild.ToString();

            //考虑到Markdown与TestPaper的混杂难以处理，应先将试题处理为html，期间还需要考虑图像引用问题。
            //注意：所有在试题影响范围内的文本均算是试题的一部分，这样试题就能支持多行文本了。

            //填空题比较复杂：
            //    ⑴如果填空题后面找不到“〓〓〓〓〓〓”结尾，则填空题只有一行文本。
            //    ⑵如果填空题后能找到试题结束标记“〓〓〓〓〓〓”，则到这个标记的所有文本均算是这个试题的内容。

            if (questionText.StartsWith("　　试题＞＞"))
            {
                questionText = "\n" + questionText;
            }

            var startIndex = questionText.IndexOf("\n　　试题＞＞");  //必须是在文本行开头才能算！

            if (startIndex < 0) return questionText;   //没有试题，原样输出。

            StringBuilder resultBuilder = new StringBuilder();
            List<Question> questions = new List<Question>();

            string temp = questionText.Replace("\n　　试题＞＞", "\n　　<<&LINE&>>试题＞＞");
            var sections = temp.Split(new string[] { "<<&LINE&>>" }, StringSplitOptions.None);

            int startNumber = 1;

            foreach (var s in sections)
            {
                if (s.StartsWith("试题＞＞"))
                {
                    //这个字段中包含试题文本，需要特殊处理

                    //截取出试题结束符后的文本，这部分不是试题的组成部分，直接附加即可。
                    string header = s;
                    string tail = "";
                    var tailIndex = s.IndexOf("〓〓〓〓〓〓");
                    if (tailIndex >= 0)
                    {
                        tail = s.Substring(tailIndex + 6);
                        header = s.Substring(0, tailIndex);
                    }
                    else
                    {
                        //if (withoutScripts == true)  // 不是只有在调用 reveal.js 时才需要这样。
                        //{
                        var options = new RegexOptions();
                        options |= System.Text.RegularExpressions.RegexOptions.Multiline;
                        var regRegion = new Regex(@"((\r\n){1,}?|\n{1,}?)", options);
                        var matchRegion = regRegion.Match(s);
                        header = s.Substring(0, matchRegion.Index);
                        tail = s.Substring(matchRegion.Index);
                        //}
                    }

                    header = ConvertQuestionsToHtml(header, ref startNumber, out int choiceExCount, out List<Question> questionList, withoutScripts, forEpub);
                    if (questionList != null && questionList.Count > 0)
                    {
                        questions.AddRange(questionList);
                    }

                    choiceQuestionsCount += choiceExCount;

                    resultBuilder.Append("\n\n");
                    resultBuilder.Append(header);
                    resultBuilder.Append("\n\n");
                    resultBuilder.Append(tail);
                }
                else
                {
                    //不需要特殊处理。
                    //Markdown中，空行、空格都是有用处的，不能随意去除。
                    //resultBuilder.Append(FormatText(s));
                    resultBuilder.Append(s);
                }
            }

            // 检验是否有题干相同的试题（选择或判断或填空）！
            var checkResult2 = QuestionBuilder.CheckSameTitleQuestionExists(questions);
            if (string.IsNullOrEmpty(checkResult2) == false)
            {
                LMessageBox.ShowWarning("请注意下列信息：\r\n==================\r\n" + checkResult2);
            }

            // 生成适用于 phpMyAdmin 的 SQL 导入文件
            SaveSQLImportFile(test_paper_id, questions, markdownFilePath);

            //Markdown中，空行、空格都是有用处的，不能随意去除。
            //return FormatText(resultBuilder.ToString());
            return resultBuilder.ToString();
        }

        /// <summary>
        /// 自动生成适用于 phpMyAdmin 导入数据表的 SQL 文件，其中包含了每道选择题的题号（假定序号总是从1开始并连续）。
        /// </summary>
        /// <param name="test_paper_id"></param>
        /// <param name="questions"></param>
        /// <param name="markdownFilePath"></param>
        private static void SaveSQLImportFile(string test_paper_id, List<Question> questions, string markdownFilePath)
        {
            // if (string.IsNullOrWhiteSpace(test_paper_id)) return;

            if (System.IO.File.Exists(markdownFilePath) == false || questions == null || questions.Count <= 0) return;

            var markdownFileInfo = new System.IO.FileInfo(markdownFilePath);
            var sqlFileInfo = new FileInfo(markdownFileInfo.Directory.FullName + "\\" + markdownFileInfo.Name.Substring(0, markdownFileInfo.Name.Length - markdownFileInfo.Extension.Length) + "~.sql");

            if (string.IsNullOrWhiteSpace(test_paper_id) || Globals.MainWindow.CompileAsTestPaper == false)
            {
                if (sqlFileInfo.Exists)
                {
                    File.Delete(sqlFileInfo.FullName); sqlFileInfo = null;
                }
                return;
            }

            var choiceQuestionsCount = 0;
            foreach (var q in questions)
            {
                if (q.Type == QuestionType.Choice)
                    choiceQuestionsCount++;
            }

            if (choiceQuestionsCount <= 0)
            {
                if (sqlFileInfo.Exists)
                {
                    File.Delete(sqlFileInfo.FullName); sqlFileInfo = null;
                }
                return;
            }

            var datetime = DateTime.Now.ToString();
            var insert_rows_text = "";
            // INSERT INTO `2023030801` (`id`, `a_points`, `b_points`, `c_points`, `d_points`, `answer`, `date`, `comment`) VALUES
            // ({ id}, '{a_points}', '{b_points}', '{c_points}', '{d_points}', '{answer}', '{test_paper_id}', '{comment}');

            var choiceQuestionNumber = 0;

            for (int i = 0; i < questions.Count; i++)
            {
                var question = questions[i];
                if (question.Type != QuestionType.Choice || question.ChoiceItems.Count != 4) continue;

                choiceQuestionNumber++;

                var id = choiceQuestionNumber.ToString();
                var a_points = question.ChoiceItems[0].CheckPoints;
                var b_points = question.ChoiceItems[1].CheckPoints;
                var c_points = question.ChoiceItems[2].CheckPoints;
                var d_points = question.ChoiceItems[3].CheckPoints;
                var answerIndex = -1;
                for (int j = 0; j < 4; j++)
                {
                    if (question.ChoiceItems[j].IsAnswer)
                    {
                        answerIndex = j;
                        break;
                    }
                }
                var answer = "";
                switch (answerIndex)
                {
                    case 0: answer = "A"; break;
                    case 1: answer = "B"; break;
                    case 2: answer = "C"; break;
                    case 3: answer = "D"; break;
                    default: answer = "X"; break;
                }
                var comment = "原题号：" + (i + 1).ToString();
                var row_text = $@"INSERT INTO `{test_paper_id}` (`id`, `a_points`, `b_points`, `c_points`, `d_points`, `answer`, `date`, `comment`) VALUES ({id}, '{a_points}', '{b_points}', '{c_points}', '{d_points}', '{answer}', '{test_paper_id}', '{comment}');";
                insert_rows_text += row_text + "\r\n";
            }


            var template = $@"
-- phpMyAdmin SQL Dump
-- version 5.2.0
-- https://www.phpmyadmin.net/
--
-- 主机： localhost
-- 生成日期： {datetime}
-- 服务器版本： 5.6.50-log
-- PHP 版本： 8.0.26

SET SQL_MODE = ""NO_AUTO_VALUE_ON_ZERO"";
START TRANSACTION;
SET time_zone = ""+00:00"";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- 数据库： `test_papers`
--

-- --------------------------------------------------------

--
-- 表的结构 `{test_paper_id}`
--

CREATE TABLE `{test_paper_id}` (
  `id` int(11) NOT NULL COMMENT '选择题题号',
  `a_points` text CHARACTER SET utf8 NOT NULL COMMENT '题支A的考查点',
  `b_points` text NOT NULL COMMENT '题支B的考查点',
  `c_points` text NOT NULL COMMENT '题支C的考查点',
  `d_points` text NOT NULL COMMENT '题支D的考查点',
  `answer` text NOT NULL COMMENT '选择题答案',
  `date` text NOT NULL COMMENT '提交日期',
  `comment` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

--
-- 转存表中的数据 `{test_paper_id}`
--

{insert_rows_text}
--
-- 转储表的索引
--

--
-- 表的索引 `{test_paper_id}`
--
ALTER TABLE `{test_paper_id}`
  ADD PRIMARY KEY (`id`);

--
-- 在导出的表使用AUTO_INCREMENT
--

--
-- 使用表AUTO_INCREMENT `{test_paper_id}`
--
ALTER TABLE `{test_paper_id}`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '选择题题号', AUTO_INCREMENT=2;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
";
            System.IO.File.WriteAllText(sqlFileInfo.FullName, template);
        }

        /// <summary>
        /// 文本格式化，去除每行首尾的空白字符，统一换行符为“\n”。
        /// </summary>
        /// <param name="sourceText">源文本。</param>
        public static string FormatText(string sourceText)
        {
            if (string.IsNullOrEmpty(sourceText)) return "";

            StringBuilder sb = new StringBuilder();
            var lines = sourceText.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var line in lines)
            {
                string s = line.Trim(new char[] { '\t', ' ', '　' });
                sb.Append(s + (s.Length <= 0 ? "" : "\n"));
            }

            var result = sb.ToString();
            if (result.StartsWith("→→") || result.StartsWith("——"))
            {
                result = "——" + result.TrimStart(new char[] { '—', '→', });
            }

            if (result.EndsWith("\n"))
            {
                return Regex.Replace(result.Substring(0, result.Length - 1), @"\<[bB][rR] {0,}?\/?\>", "<br>　　");
            }
            else return Regex.Replace(result, @"\<[bB][rR] {0,}?\/?\>", "<br>　　");
        }

        /// <summary>
        /// 将试题文本转换为Html。（试题编译）
        /// 此方法由另一个同名方法调用，本身不单独使用！！！
        /// </summary>
        /// <param name="questionText">试题文本。</param>
        /// <param name="startNumber">试题序号。</param>
        /// <param name="withoutScripts">不带Js脚本、“复制”链接、解析面板等。（用于调用 Reveal.js 演示时）</param>
        private static string ConvertQuestionsToHtml(string questionText, ref int startNumber, out int choiceExCount,
            out List<Question> newQuestions, bool withoutScripts = false, bool forEpub = false, bool choiceItemsConfounded = false)
        {
            if (string.IsNullOrEmpty(questionText)) { choiceExCount = 0; newQuestions = null; return ""; }

            questionText.Replace("<", "&lt;").Replace(">", "&gt;");

            newQuestions = BaseQuestion.BuildQuestionList(questionText, true, choiceItemsConfounded);
            if (newQuestions == null || newQuestions.Count <= 0) { choiceExCount = 0; newQuestions = null; return ""; }

            StringBuilder sb = new StringBuilder();
            StringBuilder endScript = new StringBuilder();

            var hideText = "";
            if (Globals.MainWindow.HideExamAnswer)
            {
                hideText = " style = 'display:none;'";
            }

            choiceExCount = 0;  // 选择题数量
            for (int i = 0; i < newQuestions.Count; i++)
            {
                var q = newQuestions[i];
                switch (q.Type)
                {
                    case QuestionType.Judge://判断题是特殊形式的选择题。
                    case QuestionType.Choice:
                        {
                            choiceExCount++;

                            var indexOfStart = q.TitleString.IndexOf("【");
                            var indexOfEnd = q.TitleString.IndexOf("】");
                            if (indexOfStart < 0 || indexOfEnd < 0 || indexOfEnd <= indexOfStart) continue;//选择题无填充项，则无法确定答案。

                            //var fillblank = "";
                            //fillblank = q.TitleString.Substring(indexOfStart + 1, indexOfEnd - indexOfStart - 2);

                            //var displayTitle = q.TitleString.Replace("【", "<code class='exam_fb'>").Replace("】", "</code>");
                            var displayTitle = q.TitleString.Substring(0, indexOfStart + 1) + "　　" + q.TitleString.Substring(indexOfEnd);

                            // 除试题外，其它 slide 类元素的占位符（甚至是 ID 由 JS 自行添加）
                            sb.Append($"<div id='ex_{startNumber}_placeholder' style='display:none;'>占位符</div>");
                            sb.Append($"<div class='ex_c slide' id='ex_{startNumber}' style='position: relative;' isRight='false'>");

                            //让选择题、判断题题干部分支持二维文字表。
                            // 可以调用 CompileTextTable() 方法。
                            var indexOflineBreak = displayTitle.IndexOf("\n");

                            if (q.IsCompactImageLayout == false)
                            {
                                // 非紧凑布局
                                if (indexOflineBreak < 0)
                                {
                                    sb.Append($"<p class='ex_c_t' id=\"ex_{startNumber}_t\">" + startNumber.ToString() + "．" + FormatText(displayTitle) + "</p>");
                                }
                                else
                                {
                                    var titleTail = displayTitle.Substring(indexOflineBreak);
                                    if (titleTail.Contains("|") || titleTail.Contains("｜"))
                                    {
                                        var titleHeader = displayTitle.Substring(0, indexOflineBreak);
                                        sb.Append($"<p class='ex_c_t' id=\"ex_{startNumber}_t\">" + startNumber.ToString() + "．" + FormatText(titleHeader) + "</p>");

                                        var md = new MarkdownSharp.Markdown();
                                        var tableInfoList = CustomMarkdownSupport.CompileTextTable(titleTail.Replace("\r\n", "\n").Split(new char[] { '\n', }), ref md);
                                        foreach (var ti in tableInfoList)
                                        {
                                            var span = ti.TableHtmlText;
                                            if (string.IsNullOrWhiteSpace(span) == false)
                                            {
                                                sb.Append(span);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        sb.Append($"<p class='ex_c_t' id=\"ex_{startNumber}_t\">" + startNumber.ToString() + "．" + FormatText(displayTitle) + "</p>");
                                    }
                                }

                                //嵌入标题中的图像文件链接
                                if (string.IsNullOrWhiteSpace(q.MarkdownImagePath) == false)
                                {
                                    sb.Append($"<p><img src=\"{q.MarkdownImagePath}\" alt=\"{q.MarkdownImageTooltip}\"/></p>");
                                }

                                var olStyle = "";
                                if (Globals.MainWindow.CompileAsTestPaper)
                                {
                                    olStyle = " style='margin-left:0px;'";
                                }

                                sb.Append($"<ol class='ex_c_xx'{olStyle}>");

                                if (withoutScripts == false)
                                {
                                    endScript.Append("<script>");
                                }

                                for (int m = 0; m < q.ChoiceItems.Count; m++)
                                {
                                    var choiceMark = L.ConvertToAlpha(m + 1);
                                    var item = q.ChoiceItems[m];
                                    var isAnswerMark = item.IsAnswer ? "true" : "false";

                                    if (withoutScripts == false)
                                    {
                                        sb.Append($"<li id='li_{startNumber}_{m}' ex_id='li_of_{startNumber}' style='list-style:none;text-align:left;' isAnswer='{isAnswerMark}' mark='{choiceMark}'>" + choiceMark + FormatText(item.Text) + "</li>");

                                        sb.Append($"<div class='ex_c_jx' id='jx_{startNumber}_{m}' {hideText}><p>" +
                                        (item.IsAnswer ? "<span class='ex_c_right'>答　案</span>" : "<span class='ex_c_err'>非答案</span>") +
                                        "<span class='ex_c_ana'>" + FormatText(item.AnalysisText.Replace("此项", choiceMark + "项")) + "</span></p></div>");

                                        if (Globals.MainWindow.CompileAsTestPaper && forEpub == false)
                                        {
                                            // 给选择题加上批阅功能
                                            endScript.Append(
                                                    "$(document).ready(function() {" +
                                                        $@"$('#li\_{startNumber}\_{m}').click(function() {{" +
                                                            $@"if(submitted == true){{" +
                                                                $@"$('#jx\_{startNumber}\_{m}').toggle();" +
                                                            $@"}}else{{" +
                                                                $@"var isChecked = $('#li\\_{startNumber}\\_{m}').attr('isChecked');" +
                                                                $@"if(isChecked=='true'){{" +
                                                                    $@"$('#li\\_{startNumber}\\_{m}').addClass('ex\\_user\\_checked');" +
                                                                    $@"$('#li\\_{startNumber}\\_{m}').attr('isChecked', 'false');" +
                                                                    $@"$('#ex\\_{startNumber}').attr('isRight', '');" +
                                                                    $@"return;" +
                                                                $@"}}" +
                                                            $@"$(""[ex\\_id='li\\_of\\_{startNumber}']"").removeClass('ex\\_user\\_checked');" +
                                                            $@"$(""[ex\\_id='li\\_of\\_{startNumber}']"").attr('isChecked','false');" +
                                                            //$@"$('#ex\\_{startNumber}').attr('userAnswer', $(li\\_{startNumber}\\_{m}).attr('mark'));" +
                                                            $@"if($('#li\\_{startNumber}\\_{m}').attr('isAnswer')=='true'){{ $('#ex\\_{startNumber}').attr('isRight','true'); }}else{{ $('#ex\\_{startNumber}').attr('isRight','false'); }}" +
                                                            $@"$('#li\\_{startNumber}\\_{m}').attr('isChecked', 'true');" +
                                                            $@"$('#li\\_{startNumber}\\_{m}').addClass('ex\\_user\\_checked');" +
                                                        $@"}}" +
                                                        "});" +
                                                    "});"
                                                );
                                        }
                                        else
                                        {
                                            endScript.Append(
                                                    "$(document).ready(function(){" +
                                                        $@"$('#li\_{startNumber}\_{m}').click(function(){{" +
                                                            $@"$('#jx\_{startNumber}\_{m}').toggle();" +
                                                        "});" +
                                                    "});"
                                                );
                                        }
                                    }
                                    else
                                    {
                                        if (item.IsAnswer)
                                        {
                                            sb.Append($"<li class='fragment highlight-red' id='li_{startNumber}_{m}' style='list-style:none;' isAnswer='true'>" + choiceMark + FormatText(item.Text) + "</li>");
                                        }
                                        else
                                        {
                                            sb.Append($"<li id='li_{startNumber}_{m}' style='list-style:none;' isAnswer='false'>" + choiceMark + FormatText(item.Text) + "</li>");
                                        }
                                    }
                                }
                                sb.Append("</ol>");

                                if (withoutScripts == false)
                                {
                                    if (Globals.MainWindow.CompileAsTestPaper == false || forEpub)  //用于学生做题这种模式时时不需要“复制试题”这个链接。
                                    {
                                        sb.Append($"<div class=\"question_tool_panel\" id=\"qt_Panel_{startNumber}\" style=\"text-indent:0px;position:absolute;bottom:1em;margin-left:1em;\">"
                                             + $"<a id = \"copy_ex_{startNumber}\" class=\"copy_ex\">复制</a>"
                                             + "</div>");
                                        sb.Append("</div>");
                                    }

                                    var copyChoiceQuestionScript = "$(document).ready(" +
                                                    "function(){" +
                                                      $"$('#copy\\_ex\\_{startNumber}').click(" +
                                                             "function(){" +
                                                                 "copyChoiceQuestion(" +
                                                                     $"document.getElementById('qt\\_Panel\\_{startNumber}')," +
                                                                     $"'ex\\_{startNumber}\\_t'," +
                                                                     $"'li\\_{startNumber}\\_0'," +
                                                                     $"'li\\_{startNumber}\\_1'," +
                                                                     $"'li\\_{startNumber}\\_2'," +
                                                                     $"'li\\_{startNumber}\\_3'" +
                                                                 ");" +
                                                             "}" +
                                                        ")" +
                                                    "}" +
                                               ");";
                                    endScript.Append(copyChoiceQuestionScript);
                                    endScript.Append("</script>");

                                    if (Globals.MainWindow.CompileAsTestPaper)  //用于学生做题这种模式。
                                    {
                                        sb.Append("</div>");
                                        // 多加这行会导致与六级标题冲突。具体症状为只有第1题受同级标题约束。
                                        // 但如果不加，又会导致所有试题从第一题开始逐级嵌套。
                                    }
                                }
                            }
                            else
                            {
                                // 紧凑布局
                                if (indexOflineBreak < 0)
                                {
                                    sb.Append($"<p class='ex_c_t' style='text-indent:0;' id=\"ex_{startNumber}_t\">" + startNumber.ToString() + "．" + FormatText(displayTitle) + "</p>");
                                }
                                else
                                {
                                    var titleTail = displayTitle.Substring(indexOflineBreak);
                                    if (titleTail.Contains("|") || titleTail.Contains("｜"))
                                    {
                                        var titleHeader = displayTitle.Substring(0, indexOflineBreak);
                                        sb.Append($"<p class='ex_c_t' style='text-indent:0;' id=\"ex_{startNumber}_t\">" + startNumber.ToString() + "．" + FormatText(titleHeader) + "</p>");

                                        var md = new MarkdownSharp.Markdown();
                                        var tableInfoList = CustomMarkdownSupport.CompileTextTable(titleTail.Replace("\r", "\n").Split(new char[] { '\n', }), ref md);
                                        foreach (var ti in tableInfoList)
                                        {
                                            var span = ti.TableHtmlText;
                                            if (string.IsNullOrWhiteSpace(span) == false)
                                            {
                                                sb.Append(span);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        sb.Append($"<p class='ex_c_t' style='text-indent:0;' id=\"ex_{startNumber}_t\">" + startNumber.ToString() + "．" + FormatText(displayTitle) + "</p>");
                                    }
                                }

                                sb.Append("<table cellspacing=\"0\" cellpadding=\"0\" style=\"border:0;\"><tr><td style=\"border:0;\">");
                                sb.Append("<ol class='ex_c_xx' type='A' style='margin:0px;'>");

                                if (withoutScripts == false)
                                {
                                    endScript.Append("<script>");
                                }

                                for (int m = 0; m < q.ChoiceItems.Count; m++)
                                {
                                    var choiceMark = L.ConvertToAlpha(m + 1);
                                    var item = q.ChoiceItems[m];

                                    if (withoutScripts == false)
                                    {
                                        sb.Append($"<li id='li_{startNumber}_{m}' style='text-align:left;'>" + FormatText(item.Text) + "</li>");

                                        sb.Append($"<div class='ex_c_jx' id='jx_{startNumber}_{m}'{hideText}><p>" +
                                        (item.IsAnswer ? "<span class='ex_c_right'>答　案</span>" : "<span class='ex_c_err'>非答案</span>") +
                                        "<span class='ex_c_ana'>" + FormatText(item.AnalysisText.Replace("此项", choiceMark + "项")) + "</span></p></div>");

                                        endScript.Append(
                                                "$(document).ready(function() {" +
                                                    $@"$('#li\_{startNumber}\_{m}').click(function() {{" +
                                                        $@"$('#jx\_{startNumber}\_{m}').toggle();" +
                                                    "});" +
                                                "});"
                                            );
                                    }
                                    else
                                    {
                                        if (item.IsAnswer)
                                        {
                                            sb.Append($"<li class='fragment highlight-red' id='li_{startNumber}_{m}'>" + FormatText(item.Text) + "</li>");
                                        }
                                        else
                                        {
                                            sb.Append($"<li id='li_{startNumber}_{m}'>" + FormatText(item.Text) + "</li>");
                                        }
                                    }
                                }
                                sb.Append("</ol>");

                                if (withoutScripts == false)
                                {
                                    // 紧凑布局(CompactLayout）时不添加"复制"链接.
                                    //sb.Append($"<div class=\"question_tool_panel\" id=\"qt_Panel_{startNumber}\" style=\"text-indent:0px;position:absolute;bottom:1em;margin-left:1em;\">"
                                    //         + $"<a id = \"copy_ex_{startNumber}\" class=\"copy_ex\">复制</a>"
                                    //     + "</div>");
                                    sb.Append("</div>");

                                    var copyChoiceQuestionScript = "$(document).ready(" +
                                                    "function(){" +
                                                      $"$('#copy\\_ex\\_{startNumber}').click(" +
                                                             "function(){" +
                                                                 "copyChoiceQuestion(" +
                                                                     $"document.getElementById('qt\\_Panel\\_{startNumber}')," +
                                                                     $"'ex\\_{startNumber}\\_t'," +
                                                                     $"'li\\_{startNumber}\\_0'," +
                                                                     $"'li\\_{startNumber}\\_1'," +
                                                                     $"'li\\_{startNumber}\\_2'," +
                                                                     $"'li\\_{startNumber}\\_3'" +
                                                                 ");" +
                                                             "}" +
                                                        ")" +
                                                    "}" +
                                               ");";
                                    endScript.Append(copyChoiceQuestionScript);
                                    endScript.Append("</script>");
                                }

                                sb.Append("</td><td style=\"border:0;\">");

                                //嵌入标题中的图像文件链接
                                if (string.IsNullOrWhiteSpace(q.MarkdownImagePath) == false)
                                {
                                    sb.Append($"<p style='margin:0px;'><img src=\"{q.MarkdownImagePath}\" alt=\"{q.MarkdownImageTooltip}\"/></p>");
                                }

                                sb.Append("</td></tr></table>");
                                sb.Append("</div>");
                            }

                            if (forEpub)
                            {
                                sb.Append("</div>");  // 如果少了这个，会导致 MarkdownSharp 解析极慢，甚至直接死掉。
                            }

                            startNumber++;
                            break;
                        }
                    case QuestionType.FillBlank:
                        {
                            var titleSpans = q.TitleString.Replace("【", "[[^]]【").Split(new string[] { "[[^]]", "】" }, StringSplitOptions.None);
                            var title = new StringBuilder();
                            var fillblanks = new StringBuilder();

                            int num = 0;
                            foreach (var s in titleSpans)
                            {
                                if (s.StartsWith("【"))
                                {
                                    num++;

                                    if (withoutScripts == false)
                                    {
                                        title.Append("【<u>　[" + num + "]　</u>】");
                                    }
                                    else
                                    {
                                        title.Append($"<span style=\"border-bottom: 0.1em solid;\"> <span class=\"ex_f_a fragment\">{s.Substring(1)}</span> </span>");
                                    }

                                    fillblanks.Append("[" + num + "]" + s.Substring(1));
                                    fillblanks.Append("　");
                                }
                                else
                                {
                                    title.Append(s);
                                }
                            }

                            var fillBlanksText = fillblanks.ToString();
                            if (fillBlanksText.EndsWith("　"))
                            {
                                fillBlanksText = fillBlanksText.Substring(0, fillBlanksText.Length - 1);
                            }

                            // 除试题外，其它 slide 类元素的占位符（甚至是 ID 由 JS 自行添加）
                            sb.Append($"<div id='ex_{startNumber}_placeholder' style='display:none;'>占位符</div>");
                            sb.Append($"<div class='ex_f slide' id='ex_{startNumber}'>");
                            sb.Append($"<p class='ex_f_t' id='ex_{startNumber}_t'>" + startNumber.ToString() + "．" + FormatText(title.ToString()) + "</p>");

                            //嵌入标题中的图像文件链接
                            if (string.IsNullOrWhiteSpace(q.MarkdownImagePath) == false)
                            {
                                sb.Append($"<p><img src=\"{q.MarkdownImagePath}\" alt=\"{q.MarkdownImageTooltip}\"/></p>");
                            }

                            if (withoutScripts == false)
                            {
                                sb.Append($"<p class='ex_f_da'>　<a id='copy_ex_{startNumber}'>复制</a>　<span class='ex_f_da' id='span_ah_{startNumber}'>答案</span><span id='span_at_{startNumber}'{hideText}>" + fillBlanksText + "</span></p>");

                                endScript.Append("<script>");
                                endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#span\_ah\_{startNumber}').click(function() {{" +
                                                    $@"$('#span\_at\_{startNumber}').toggle();" +
                                                "});" +
                                            "});"
                                        );

                                var copyFillBlankQuestionScript = "$(document).ready(" +
                                                                       "function(){" +
                                                                         $"$('#copy\\_ex\\_{startNumber}').click(" +
                                                                                "function(){" +
                                                                                    "copyFillBlankQuestion(" +
                                                                                        $"document.getElementById('ex\\_{startNumber}')," +
                                                                                        $"'ex\\_{startNumber}\\_t'" +
                                                                                    ");" +
                                                                                "}" +
                                                                           ")" +
                                                                       "}" +
                                                                  ");";
                                endScript.Append(copyFillBlankQuestionScript);
                                endScript.Append("</script>");
                            }

                            sb.Append("</div>");

                            startNumber++;
                            break;
                        }
                    case QuestionType.Subjective:
                        {
                            // 除试题外，其它 slide 类元素的占位符（甚至是 ID 由 JS 自行添加）
                            sb.Append($"<div id='ex_{startNumber}_placeholder' style='display:none;'>占位符</div>");
                            sb.Append($"<div class='ex_m' id='ex_{startNumber}'>");

                            if (q.Materials != null && q.Materials.Count > 0)
                            {
                                var title = q.TitleString.Trim();
                                if (string.IsNullOrWhiteSpace(title))
                                {
                                    title = "．阅读下列材料：";
                                }
                                else
                                {
                                    title = "．" + title.TrimStart(new char[] { '.', '．' });
                                }
                                sb.Append("<p class='ex_m_dy'>" + startNumber.ToString() + title + "</p>");
                            }
                            else
                            {
                                sb.Append("<p class='ex_m_dy'>" + startNumber.ToString() + "．</p>");
                            }

                            sb.Append("<div class='ex_m_cl'>\n");

                            int clNum = 1;
                            foreach (var s in q.Materials)
                            {
                                if (s.StartsWith("＜＜材料＞＞"))
                                {
                                    var subs = s.Substring(6).Trim(new char[] { '\r', '\n' });
                                    if (string.IsNullOrWhiteSpace(subs)) continue;
                                }

                                int indexOfcc = s.IndexOf("＜＜出处＞＞");
                                // 有出处
                                if (indexOfcc >= 0)
                                {
                                    string cl = FormatText(s.Substring(0, indexOfcc));
                                    var ignoreNumber = false;

                                    if (cl.StartsWith("＜＜材料＞＞"))
                                    {
                                        cl = cl.Substring(6).Trim(new char[] { ' ', '　', '\t', });
                                        if (cl.startswith("^ ") || cl.StartsWith("^　") || cl.StartsWith("^\t"))
                                        {
                                            ignoreNumber = true;
                                            cl = cl.Substring(2).TrimStart();
                                        }
                                    }

                                    string fstLine;
                                    var indexOfLine = cl.IndexOf("\n");
                                    if (indexOfLine >= 0)
                                    {
                                        fstLine = cl.Substring(0, indexOfLine).TrimEnd(new char[] { '\r', '\n' });
                                        cl = cl.Substring(indexOfLine + 1);
                                    }
                                    else
                                    {
                                        fstLine = cl;
                                    }

                                    if (CustomMarkdownSupport.IsImageLinkLine(fstLine, out string imgTitle, out string imgPath))
                                    {
                                        //if (q.Materials.Count > 1)
                                        //{
                                        //    // sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + imgTitle + "</p>");  单行图像链接不适合单独作为一项材料计数
                                        //    sb.Append("<p class='ex_m_cl' style='text-align:center;text-indent:0px;'>" + cl + "</p>");  // 单行图像链接作为材料，居中
                                        //}
                                        //else
                                        //{
                                        //sb.Append("<p class='ex_m_cl' style='text-align:center;text-indent:0px;'>" + cl + "</p>");  // 单行图像链接作为材料，居中
                                        //}
                                        //clNum++;

                                        // 2024年4月19日，取消单行图像链接计数，不再将单行图像链接视为一则实体材料。
                                        //if (q.Materials.Count > 1)
                                        //{
                                        //    if (string.IsNullOrWhiteSpace(imgTitle) == false && imgTitle != "^")
                                        //    {
                                        //        if (imgTitle.EndsWith("：") == false)
                                        //        {
                                        //            imgTitle += "：";
                                        //        }
                                        //        sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + imgTitle + "</p>");
                                        //    }
                                        //    else
                                        //    {
                                        //        sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "</p>");
                                        //    }
                                        //}
                                        //else
                                        //{
                                        //    sb.Append("<p class='ex_m_cl'>" + cl + "</p>");
                                        //}
                                        //clNum++;

                                        // 2024年4月19日，取消单行图像链接计数，不再将单行图像链接视为一则实体材料。
                                        // sb.Append("<p class='ex_m_cl' style='text-align:center;text-indent:0px;'>" + cl + "</p>");  // 单行图像链接作为材料，居中
                                        Regex regImagePath = new Regex(@"(?<=(^!\[.*\]\()).{1,}(?=([ 　\t]*.*?\)))");

                                        var regStyle = new Regex(@"(?<=(style\=[\\""\'])).+(?=[\\""\'])");
                                        var matchStyle = regStyle.Match(fstLine);
                                        if (matchStyle.Success)
                                        {
                                            sb.Append($"<img src='{imgPath}' style=\"{matchStyle.Value}\"/>\n");
                                        }
                                        else
                                        {
                                            sb.Append($"<img src='{imgPath}'/>\n");
                                        }

                                        if (string.IsNullOrWhiteSpace(imgTitle) == false && imgTitle != "^")
                                        {
                                            sb.Append($"<p class='ex_cl_img_title'>{imgTitle}</p>");
                                        }
                                        continue;
                                    }

                                    // 材料首行可以是个图像，其余行可以是表格。
                                    if (cl.Contains("｜") || cl.Contains("|"))
                                    {
                                        var clines = cl.Replace("\r", "\n").Replace("｜", "|").Split(new char[] { '\n', }, StringSplitOptions.RemoveEmptyEntries);
                                        int tableStartIndex = -1;
                                        for (int icl = 0; icl < clines.Length; icl++)
                                        {
                                            var cline = clines[icl];
                                            if (cline.Contains("|"))
                                            {
                                                tableStartIndex = icl;
                                                break;
                                            }
                                        }

                                        if (tableStartIndex < 0)
                                        {
                                            sb.Append("<p class='ex_m_cl'>" + cl + "</p>");
                                        }
                                        else
                                        {
                                            for (int icl = 0; icl < tableStartIndex; icl++)
                                            {
                                                sb.Append("<p class='ex_m_cl'>" + clines[icl] + "</p>");
                                            }

                                            var sbTable = new StringBuilder();

                                            for (int icl = tableStartIndex; icl < clines.Length; icl++)
                                            {
                                                sbTable.Append(clines[icl]);
                                                sbTable.Append("\r\n");
                                            }
                                            var md = new MarkdownSharp.Markdown();
                                            var tableInfoList = CustomMarkdownSupport.CompileTextTable(sbTable.ToString().Split(new string[] { "\r\n", }, StringSplitOptions.None), ref md);
                                            foreach (var ti in tableInfoList)
                                            {
                                                var span = ti.TableHtmlText;
                                                if (string.IsNullOrWhiteSpace(span) == false)
                                                {
                                                    sb.Append(span);
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (q.Materials.Count > 1)
                                        {
                                            if (ignoreNumber == false)
                                            {
                                                sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + cl + "</p>");
                                            }
                                            else
                                            {
                                                sb.Append("<p class='ex_m_cl_n'>" + cl + "</p>");
                                            }
                                        }
                                        else
                                        {
                                            sb.Append("<p class='ex_m_cl'>" + cl + "</p>");
                                        }
                                        if (ignoreNumber == false) clNum++;
                                    }

                                    string clcc = s.Substring(indexOfcc + 6);
                                    if (clcc.StartsWith("——") == false && clcc.StartsWith("→→") == false)      // FormatText() 方法不是专用于处理材料题出处的，所以这里不能省略。
                                    {
                                        clcc = "——" + clcc.Trim(new char[] { '→', '—', ' ', '　' });
                                    }

                                    clcc = FormatText(clcc);

                                    if (string.IsNullOrEmpty(clcc.Trim(new char[] { ' ', '\t', '　', '—', })) == false)
                                    {
                                        sb.Append("<p class='ex_m_cc'>" + clcc + "</p>");
                                    }
                                }
                                else
                                {
                                    // 无出处
                                    var cl = FormatText(s);
                                    var ignoreNumber = false;
                                    if (cl.StartsWith("＜＜材料＞＞"))
                                    {
                                        cl = cl.Substring(6);
                                        if (cl.StartsWith("^ ") || cl.StartsWith("^　") || cl.StartsWith("^\t"))
                                        {
                                            ignoreNumber = true;
                                            cl = cl.Substring(2).TrimStart();
                                        }
                                    }

                                    string fstLine;
                                    var indexOfLine = cl.IndexOf("\n");
                                    if (indexOfLine >= 0)
                                    {
                                        fstLine = cl.Substring(0, indexOfLine).TrimEnd(new char[] { '\r', '\n' });
                                        cl = cl.Substring(indexOfLine + 1);
                                    }
                                    else
                                    {
                                        fstLine = cl;
                                    }

                                    if (CustomMarkdownSupport.IsImageLinkLine(fstLine, out string imgTitle))
                                    {
                                        // 2024年4月19日，取消单行图像链接计数，不再将单行图像链接视为一则实体材料。

                                        //if (q.Materials.Count > 1)
                                        //{
                                        //    if (string.IsNullOrWhiteSpace(imgTitle) == false && imgTitle != "^")
                                        //    {
                                        //        if (imgTitle.EndsWith("：") == false)
                                        //        {
                                        //            imgTitle += "：";
                                        //        }
                                        //        sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + imgTitle + "</p>");
                                        //    }
                                        //    else
                                        //    {
                                        //        sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "</p>");
                                        //    }
                                        //}
                                        //else
                                        //{
                                        //    sb.Append("<p class='ex_m_cl'>" + cl + "</p>");
                                        //}
                                        //clNum++;

                                        // sb.Append("<p class='ex_m_cl' style='text-align:center;text-indent:0px;'>" + cl + "</p>");  // 单行图像链接作为材料，居中
                                        Regex regImagePath = new Regex(@"(?<=(^!\[.*\]\()).{1,}(?=([ 　\t]*.*?\)))");
                                        var matchImagePath = regImagePath.Match(fstLine);
                                        if (matchImagePath.Success)
                                        {
                                            sb.Append($"<img src='{matchImagePath.Value}'/>\n");
                                        }

                                        if (string.IsNullOrWhiteSpace(imgTitle) == false && imgTitle != "^")
                                        {
                                            sb.Append($"<p class='ex_cl_img_title'>{imgTitle}</p>");
                                        }
                                    }
                                    else
                                    {

                                        // 材料首行可以是个图像，其余行可以是表格。
                                        if (cl.Contains("｜") || cl.Contains("|"))
                                        {
                                            if (string.IsNullOrWhiteSpace(fstLine))
                                            {
                                                if (q.Materials.Count > 1)
                                                {
                                                    sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + fstLine.Trim() + "</p>");
                                                    clNum++;
                                                }
                                                else
                                                {
                                                    sb.Append("<p class='ex_m_cl'>" + fstLine.Trim() + "</p>");
                                                }
                                            }
                                            else
                                            {
                                                if (q.Materials.Count > 1)
                                                {
                                                    sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + fstLine.Trim() + "</p>");
                                                    clNum++;
                                                }
                                                else
                                                {
                                                    sb.Append("<p class='ex_m_cl'>" + fstLine.Trim() + "</p>");
                                                }
                                            }

                                            var clines = cl.Replace("\r", "\n").Replace("｜", "|").Split(new char[] { '\n', }, StringSplitOptions.RemoveEmptyEntries);
                                            int tableStartIndex = -1;
                                            for (int icl = 0; icl < clines.Length; icl++)
                                            {
                                                var cline = clines[icl];
                                                if (cline.Contains("|"))
                                                {
                                                    tableStartIndex = icl;
                                                    break;
                                                }
                                            }

                                            if (tableStartIndex < 0)
                                            {
                                                sb.Append("<p class='ex_m_cl'>" + cl + "</p>");
                                            }
                                            else
                                            {
                                                for (int icl = 0; icl < tableStartIndex; icl++)
                                                {
                                                    sb.Append("<p class='ex_m_cl'>" + clines[icl] + "</p>");
                                                }

                                                var sbTable = new StringBuilder();

                                                for (int icl = tableStartIndex; icl < clines.Length; icl++)
                                                {
                                                    sbTable.Append(clines[icl]);
                                                    sbTable.Append("\r\n");
                                                }
                                                var md = new MarkdownSharp.Markdown();
                                                var tableInfoList = CustomMarkdownSupport.CompileTextTable(sbTable.ToString().Split(new string[] { "\r\n", }, StringSplitOptions.None), ref md);
                                                foreach (var ti in tableInfoList)
                                                {
                                                    var span = ti.TableHtmlText;
                                                    if (string.IsNullOrWhiteSpace(span) == false)
                                                    {
                                                        sb.Append(span);
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (q.Materials.Count > 1)
                                            {
                                                if (ignoreNumber == false)
                                                {
                                                    sb.Append("<p class='ex_m_cl'>" + "材料" + DateTimeConverter.YearToCn(clNum) + "　" + cl + "</p>");
                                                }
                                                else
                                                {
                                                    sb.Append("<p class='ex_m_cl_n'>" + cl + "</p>");
                                                }
                                            }
                                            else
                                            {
                                                sb.Append("<p class='ex_m_cl'>" + cl + "</p>");
                                            }

                                            if (ignoreNumber == false) clNum++;
                                        }
                                    }
                                }
                            }
                            sb.Append("</div>");//<div class='ex_m_cl'>，材料块。

                            sb.Append("<p class='ex_m_tip'>请回答：</p>");

                            //问题部分
                            for (int j = 0; j < q.AsksList.Count; j++)
                            {
                                var s = q.AsksList[j];
                                var qText = (q.AsksList.Count > 1 ? ("(" + (j + 1).ToString() + ")") : "") + FormatText(s);
                                sb.Append("<p class='ex_m_q'>" + qText + "<p>");
                            }

                            if (withoutScripts == false)
                            {
                                sb.Append($"<p class='ex_m_dy'><span class='ex_m_answer' id='ex_m_answer_{startNumber}'>参考答案</span></p>");
                            }

                            //下面是答案部分。

                            for (int j = 0; j < q.Answers.Count; j++)
                            {
                                var s = q.Answers[j];
                                int indexOfjx = s.IndexOf("　　解析＞＞");

                                if (withoutScripts == false)
                                {
                                    endScript.Append("<script>");
                                }

                                if (indexOfjx >= 0)
                                {
                                    //注意：Markdown编译器在处理带下划线的字符串时遵循如下规则：
                                    //      如果该下划线是在某个Html Tag的内部，则原样输出；
                                    //      如果该下划线不是某个Html Tag的内部（例如在Js代码中），要转义才能输出，不转义会被解释为倾斜效果。


                                    if (withoutScripts == false)
                                    {
                                        sb.Append($"<p class='ex_m_da'{hideText} id='ex_m_da_{startNumber}_{j}'>" + (q.Answers.Count > 1 ? ("(" + (j + 1).ToString() + ")") : "") + FormatText(s.Substring(0, indexOfjx)) + "</p>");
                                        sb.Append($"<p class='ex_m_jx'{hideText} id='ex_m_jx_{startNumber}_{j}'><span class='ex_m_analysis'>解析</span>" + FormatText(s.Substring(indexOfjx + 6)) + "</p>");
                                    }
                                    else
                                    {
                                        sb.Append($"<p class='ex_m_da fragment fade-up' id='ex_m_da_{startNumber}_{j}'>" + (q.Answers.Count > 1 ? ("(" + (j + 1).ToString() + ")") : "") + "答：" + FormatText(s.Substring(0, indexOfjx)) + "</p>");
                                    }

                                    if (withoutScripts == false)
                                    {
                                        endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#ex\_m\_da\_{startNumber}\_{j}').click(function() {{" +
                                                    $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                "});" +
                                            "});"
                                        );//点击隐藏自身
                                        endScript.Append(
                                                   "$(document).ready(function() {" +
                                                       $@"$('#ex\_m\_jx\_{startNumber}\_{j}').click(function() {{" +
                                                           $@"$('#ex\_m\_jx\_{startNumber}\_{j}').toggle();" +
                                                       "});" +
                                                   "});"
                                               );//点击隐藏自身
                                        endScript.Append(
                                                "$(document).ready(function() {" +
                                                    $@"$('#ex\_m\_answer\_{startNumber}').click(function() {{" +
                                                        $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                        $@"$('#ex\_m\_jx\_{startNumber}\_{j}').toggle();" +
                                                    "});" +
                                                "});"
                                            );//点击显示或隐藏下属的答案、解析
                                    }
                                }
                                else
                                {
                                    if (withoutScripts == false)
                                    {
                                        sb.Append($@"<p class='ex_m_da'{hideText} id='ex_m_da_{startNumber}_{j}'>" + (q.Answers.Count > 1 ? ("(" + (j + 1).ToString() + ")") : "") + FormatText(s) + "</p>");

                                        endScript.Append(
                                            "$(document).ready(function() {" +
                                                $@"$('#ex\_m\_da\_{startNumber}\_{j}').click(function() {{" +
                                                    $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                "});" +
                                            "});"
                                           );//点击隐藏自身
                                        endScript.Append(
                                                "$(document).ready(function() {" +
                                                    $@"$('#ex\_m\_answer\_{startNumber}').click(function() {{" +
                                                        $@"$('#ex\_m\_da\_{startNumber}\_{j}').toggle();" +
                                                    "});" +
                                                "});"
                                            );//点击显示或隐藏下属的答案、解析
                                    }
                                    else
                                    {
                                        sb.Append($@"<p class='ex_m_da fragment fade-up' id='ex_m_da_{startNumber}_{j}'>" + (q.Answers.Count > 1 ? ("(" + (j + 1).ToString() + ")") : "") + FormatText(s) + "</p>");
                                    }
                                }

                                if (withoutScripts == false)
                                {
                                    endScript.Append("</script>");
                                }
                            }

                            sb.Append("</div>");

                            startNumber++;
                            break;
                        }
                    case QuestionType.Text:
                    default:
                        {
                            return questionText;
                        }
                }
            }

            var end = endScript.ToString();
            sb.Append(end);

            return sb.ToString();
        }

        /// <summary>
        /// 此方法用于从 Markdown 格式的图像链接文本中取出链接到的图像的路径。
        /// 试题题干中可以嵌入 Markdown 格式的图像链接。这样试题就可以支持图像了。
        /// </summary>
        /// <param name="imageLinkText">Markdown 格式的图像链接文本。</param>
        /// <returns>去除其它信息，只保留图像地址。</returns>
        public static string GetMarkdownIamgePath(string imageLinkText)
        {
            if (string.IsNullOrWhiteSpace(imageLinkText)) return null;

            //Regex regex = new Regex("(?<=\\!\\[.*\\]\\().*(?=[ \"\\)])");

            int leftBracketIndex;
            int rightBracketIndex;

            leftBracketIndex = imageLinkText.IndexOf("](");

            if (leftBracketIndex < 0) return null;

            rightBracketIndex = imageLinkText.IndexOf(" \"", leftBracketIndex);

            if (rightBracketIndex < 0)
                rightBracketIndex = imageLinkText.IndexOf(")", leftBracketIndex);

            if (rightBracketIndex < 0) return null;
            if (rightBracketIndex <= leftBracketIndex) return null;

            return imageLinkText.Substring(leftBracketIndex, rightBracketIndex - leftBracketIndex).Trim(new char[] { ' ', '　' });
        }

        /// <summary>
        /// 取 Markdown 图像链接文本。用在题干文本中提取图像链接。
        /// 格式如下： 
        ///     ![图像标题](链接 "提示文本")
        /// </summary>
        /// <param name="text">题干文本。</param>
        /// <param name="header">传出 Markdown 图像链接中的图像标题。</param>
        /// <param name="path">传出 Markdown 图像链接中的链接。</param>
        /// <param name="tooltip">传出 Markdown 图像链接中的提示部分。</param>
        /// <returns>顺利完成时返回 string.Empty。</returns>
        public static string GetMarkdownImageLinkText(string text, out string header, out string path,
            out string tooltip, ref string titleString)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                header = string.Empty;
                path = string.Empty;
                tooltip = string.Empty;
                return string.Empty;
            }

            var start = text.IndexOf("![");
            if (start < 0)
            {
                header = string.Empty;
                tooltip = string.Empty;
                path = string.Empty;
                return string.Empty;
            }

            var end = text.IndexOf(")", start);
            if (end < 0)
            {
                header = string.Empty;
                tooltip = string.Empty;
                path = string.Empty;
                return string.Empty;
            }

            var result = text.Substring(start, end - start) + ")";
            var index = result.IndexOf("](");
            if (index < 0)
            {
                header = string.Empty;
                tooltip = string.Empty;
                path = string.Empty;
                return string.Empty;
            }

            header = result.Substring(2, index - 2);
            var newEnd = result.IndexOf(" \"", index);
            if (newEnd < 0)
            {
                newEnd = result.Length;
                path = result.Substring(index + 2, newEnd - index - 3).Trim(new char[] { ' ', '　', '\t' });//最后是个")"。
                tooltip = string.Empty;
            }
            else
            {
                path = result.Substring(index + 2, newEnd - index - 2).Trim(new char[] { ' ', '　', '\t' });//最后是个")"。
                var newEnd2 = result.IndexOf("\")", newEnd);
                if (newEnd2 < 0 || newEnd2 < newEnd + 2)
                {
                    tooltip = string.Empty;
                }
                else
                {
                    tooltip = result.Substring(newEnd + 2, newEnd2 - newEnd - 2);
                }
            }

            if (start >= 0) titleString = titleString.Substring(0, start);
            return result;
        }

        #region 主观题使用的几个列表。

        private List<string> materials = new List<string>();
        /// <summary>
        /// 材料列表。
        /// </summary>
        public List<string> Materials { get { return this.materials; } }

        private List<Material> materialList = new List<Material>();
        /// <summary>
        /// 材料列表。
        /// </summary>
        public List<Material> MaterialList { get { return materialList; } }

        private List<string> asksList = new List<string>();
        /// <summary>
        /// 问题列表。
        /// </summary>
        public List<string> AsksList { get { return this.asksList; } }

        private List<string> answers = new List<string>();
        /// <summary>
        /// [只读]此属性仅适用于主观题。
        /// </summary>
        public List<string> Answers { get { return this.answers; } }

        #endregion


        private string titleString;
        /// <summary>
        /// 题干文本。
        /// </summary>
        public string TitleString
        {
            get
            {
                string result = this.titleString;
                var regexNumber = new Regex(@"^[\(（]\.\d{0,}\.[\)）]");
                var matchNumber = regexNumber.Match(result);
                if (matchNumber.Success)
                {
                    result = result.Substring(matchNumber.Length);
                }

                Regex regex = new Regex(@"!\[.*\]\(.*\)");
                var match = regex.Match(this.titleString);
                if (match != null && match.Success)
                {
                    string shortName = "下图";
                    var link = match.Value;
                    Regex regex2 = new Regex(@"\[.*\]");
                    var martch2 = regex2.Match(link);
                    if (martch2 != null && martch2.Success && martch2.Length > 2)
                    {
                        shortName = martch2.Value.Substring(1, martch2.Length - 2);
                    }
                    result = this.titleString.Substring(0, match.Index) + "<p style=\"text-indent:0px; text-align: center;\">《" + shortName + "》</p>" + this.titleString.Substring(match.Index + match.Length);
                    return result;
                }

                return result;
            }
            set
            {
                this.titleString = value;

                string header;
                string path;
                string tooltip;

                this.markdownLinkText = GetMarkdownImageLinkText(value, out header, out path, out tooltip, ref titleString);
                this.markdownImageHeader = header;
                this.markdownImagePath = path;
                this.markdownIamgeTooltip = tooltip;

                解析二维文字表(this.TitleString);
            }
        }

        private void 解析二维文字表(string text)
        {
            if (string.IsNullOrWhiteSpace(text)) return;

            var lines = text.Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
            var sb = new StringBuilder();
            var linesCount = 0;
            foreach (var line in lines)
            {
                if (line.IndexOf("｜") >= 0 || line.IndexOf("|") >= 0)
                {
                    sb.Append(line + "\r\n");
                    linesCount++;
                }
            }

            if (linesCount > 0)
            {
                this._题干文字表 = new 文字表(sb.ToString());
            }
            else
            {
                this._题干文字表 = null;
            }
        }

        public string 题干文本
        {
            get { return TitleString.Trim(new char[] { ' ', '　', '\t', '\r', '\n' }); }
            set { TitleString = value; }
        }

        public string 编号题干文本
        {
            get
            {
                var orderText = OrderNumber.ToString();
                if (string.IsNullOrWhiteSpace(orderText))
                {
                    return TitleString.Trim(new char[] { ' ', '　', '\t', '\r', '\n' });
                }

                return orderText + "．" + TitleString.Trim(new char[] { ' ', '　', '\t', '\r', '\n' });
            }
        }

        private 文字表 _题干文字表 = null;
        /// <summary>
        /// 目前（2025.1.8）只有“取选择题列表”方法才会使用它。
        /// </summary>
        public 文字表 题干文字表
        {
            get { return _题干文字表; }
            set { _题干文字表 = value; }
        }

        private string markdownImagePath = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的路径。
        /// </summary>
        public string MarkdownImagePath { get { return this.markdownImagePath; } }

        private string markdownImageHeader = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的标题文本。
        /// </summary>
        public string MarkdownIamgeHeader { get { return this.markdownImageHeader; } }

        private string markdownIamgeTooltip = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的提示文本。
        /// </summary>
        public string MarkdownImageTooltip
        {
            get
            {
                return this.markdownIamgeTooltip;
            }
        }

        /// <summary>
        /// 图像是否使用紧凑布局。（默认布局图像在题干文本与题支列表之间，比较占地方。图像尺寸较小时，可以指定使用紧凑布局（标题文本以“-”开头）。
        /// </summary>
        public bool IsCompactImageLayout { get { return this.markdownImageHeader.Replace("－", "-").Replace("—", "-").StartsWith("--"); } }

        public bool 是否使用紧凑布局
        {
            get { return IsCompactImageLayout; }
        }

        private string markdownLinkText = "";
        /// <summary>
        /// 通过 Markdown 格式引用的图像链接的完全文本。形如：
        ///     ![图像标题](引用路径 "提示文本")
        /// </summary>
        public string MarkdownLinkText { get { return this.markdownLinkText; } }

        /// <summary>
        /// 复制到其它编辑器时转换而成的新图像链接文本。
        /// </summary>
        public string NewMarkdownLinkText { get; set; }

        /// <summary>
        /// 试题引用的图像的物理磁盘路径。
        /// </summary>
        public string MarkdownImageLinkRealPath { get; set; } = null;

        public string 图像完整路径
        {
            get
            {
                if (string.IsNullOrWhiteSpace(MarkdownImageLinkRealPath))
                    return "";

                return MarkdownImageLinkRealPath;
            }
            set
            {
                MarkdownImageLinkRealPath = value;
            }
        }
        public string 完整图像路径
        {
            get
            {
                if (string.IsNullOrWhiteSpace(MarkdownImageLinkRealPath))
                    return "";

                return MarkdownImageLinkRealPath;
            }
            set
            {
                MarkdownImageLinkRealPath = value;
            }
        }

        private int orderNumber = 0;
        /// <summary>
        /// 用于给试题随机排序。
        /// </summary>
        public int OrderNumber
        {
            get { return orderNumber; }
            set { this.orderNumber = value; }
        }

        public int 试题编号
        {
            get { return orderNumber; }
            set { orderNumber = value; }
        }

        public string OutText(bool replaceWithNewMarkdownLink = false)
        {
            switch (Type)
            {
                case QuestionType.Choice:
                case QuestionType.Judge:
                    {
                        var sb = new StringBuilder();
                        sb.Append("　　试题＞＞");
                        sb.Append(TitleString.Trim(new char[] { '\r', '\n', }));
                        sb.Append("\r\n\r\n");

                        var imgLink = (replaceWithNewMarkdownLink ? NewMarkdownLinkText : MarkdownLinkText);
                        if (string.IsNullOrWhiteSpace(imgLink) == false)
                        {
                            sb.Append(imgLink);
                            sb.Append("\r\n\r\n");
                        }

                        foreach (var ci in ChoiceItems)
                        {
                            if (ci.IsAnswer)
                            {
                                sb.Append("　　答案＞＞");
                                sb.Append(ci.Text);
                                sb.Append("\r\n");
                                if (string.IsNullOrWhiteSpace(ci.AnalysisText) == false)
                                {
                                    sb.Append("　　解析＞＞");
                                    sb.Append(ci.AnalysisText);
                                    sb.Append("\r\n\r\n");
                                }
                                else sb.Append("\r\n");
                            }
                        }
                        foreach (var ci in ChoiceItems)
                        {
                            if (ci.IsAnswer == false)
                            {
                                sb.Append("　　错项＞＞");
                                sb.Append(ci.Text);
                                sb.Append("\r\n");
                                if (string.IsNullOrWhiteSpace(ci.AnalysisText) == false)
                                {
                                    sb.Append("　　解析＞＞");
                                    sb.Append(ci.AnalysisText);
                                    sb.Append("\r\n\r\n");
                                }
                                else sb.Append("\r\n");
                            }
                        }
                        sb.Append("〓〓〓〓〓〓");
                        sb.Append("\r\n\r\n");
                        return sb.ToString();
                    }
                case QuestionType.FillBlank:
                    {
                        var sb = new StringBuilder();
                        sb.Append("　　试题＞＞");
                        sb.Append(TitleString.Trim(new char[] { '\r', '\n', }));
                        sb.Append("\r\n\r\n");

                        var imgLink = (replaceWithNewMarkdownLink ? NewMarkdownLinkText : MarkdownLinkText);
                        if (string.IsNullOrWhiteSpace(imgLink) == false)
                        {
                            sb.Append(imgLink);
                            sb.Append("\r\n\r\n");
                        }

                        return sb.ToString();
                    }
                case QuestionType.Subjective:
                    {
                        var trimChars = new char[] { '\r', '\n', ' ', '　', '\t', };
                        var sb = new StringBuilder();
                        sb.Append("　　试题＞＞请阅读材料并回答问题：\r\n");
                        foreach (var m in this.Materials)
                        {
                            if (m.startswith("＜＜材料＞＞"))
                            {
                                var subm = m.Substring(6).Trim(new char[] { '\r', '\n' });
                                if (string.IsNullOrWhiteSpace(subm))
                                {
                                    continue;
                                }
                            }

                            var index = m.IndexOf("＜＜出处＞＞");
                            if (index >= 0)
                            {
                                // sb.Append("＜＜材料＞＞");
                                sb.Append(m.Substring(0, index).Trim(trimChars));
                                sb.Append("\r\n");
                                // sb.Append("＜＜出处＞＞");
                                sb.Append(m.Substring(index + 6).Trim(trimChars));
                                sb.Append("\r\n\r\n");
                            }
                            else
                            {
                                // sb.Append("＜＜材料＞＞");
                                sb.Append(m.Trim(trimChars));
                                sb.Append("\r\n\r\n");
                            }
                        }
                        // sb.Append("请回答：\r\n");  // 这行是不能在这里添加的。
                        if (this.AsksList.Count >= this.Answers.Count)
                        {
                            for (int x = 0; x < this.AsksList.Count; x++)
                            {
                                sb.Append("＜＜问题＞＞");
                                sb.Append(this.AsksList[x].Trim(trimChars));
                                if (x < this.Answers.Count)
                                {
                                    sb.Append("\r\n");
                                    sb.Append("　　答案＞＞");
                                    sb.Append(this.Answers[x].Trim(trimChars));
                                    sb.Append("\r\n\r\n");
                                }
                                else
                                {
                                    sb.Append("\r\n\r\n");
                                }
                            }
                            sb.Append("〓〓〓〓〓〓\r\n\r\n");
                        }
                        return sb.ToString();
                    }
                // case QuestionType.Text:
                default: return TitleString;
            }
        }
    }

    public class Material
    {
        /// <summary>
        /// 材料文本。
        /// </summary>
        public string MaterialText { get; set; }

        private string materialSource = "";
        /// <summary>
        /// 材料出处。
        /// </summary>
        public string MaterialSource
        {
            get
            {
                if (string.IsNullOrWhiteSpace(materialSource)) return null;
                if (materialSource.StartsWith("——") == false)
                {
                    materialSource = "——" + materialSource;
                }
                return materialSource;
            }
            set { materialSource = value; }
        }
    }

    /// <summary>
    /// 支持的试题类别。
    /// </summary>
    public enum QuestionType
    {
        /// <summary>
        /// 选择题。
        /// </summary>
        Choice,

        /// <summary>
        /// 填空题
        /// </summary>
        FillBlank,

        /// <summary>
        /// 判断题。
        /// </summary>
        Judge,

        /// <summary>
        /// 主观题。
        /// </summary>
        Subjective,

        /// <summary>
        /// 非试题，只是文本。
        /// </summary>
        Text,
    }
}
